﻿using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using Unicorn.Internal;
using Unicorn;
using Unicorn.Arm;
using System;
using System.Runtime.InteropServices;

    public unsafe class Uc:Libc
    {
    public static ulong SDL_GetTicks64()
    {
        throw new NotImplementedException();
    }
    public static uc_err UC_ERR_OK = uc_err.UC_ERR_OK;
    public static int UC_MIPS_REG_A0= (int)UC_MIPS_REG.UC_MIPS_REG_4;
    public static int UC_MIPS_REG_RA = 0;
    public static int UC_MIPS_REG_PC = 0;
    public static int UC_MIPS_REG_A1= 0;
    public static int UC_MIPS_REG_A2 = 0;
    public static int UC_MIPS_REG_A3 = 0;
    public static int UC_MIPS_REG_S0 = 0;
    public static int UC_MIPS_REG_S1 = 0;
    public static int UC_MIPS_REG_S2 = 0;
    public static int UC_MIPS_REG_S3 = 0;
    public static int UC_MIPS_REG_S4 = 0;
    public static int UC_MIPS_REG_S5 = 0;
    public static int UC_MIPS_REG_S6 = 0;
    public static int UC_MIPS_REG_S7 = 0;
    public static int UC_MIPS_REG_LO = 0;
    public static int UC_MIPS_REG_SP = 0;
    public static int UC_MIPS_REG_FP = 0;
    public static int UC_MIPS_REG_AT = 0;
    public static int UC_MIPS_REG_V0 = 0;
    public static int UC_MIPS_REG_V1 = 0;
    public static int UC_MIPS_REG_HI = 0;
    enum UC_MIPS_REG
    {
        UC_MIPS_REG_INVALID = 0,
        //> General purpose registers
        UC_MIPS_REG_PC,

        UC_MIPS_REG_0,
        UC_MIPS_REG_1,
        UC_MIPS_REG_2,
        UC_MIPS_REG_3,
        UC_MIPS_REG_4,
        UC_MIPS_REG_5,
        UC_MIPS_REG_6,
        UC_MIPS_REG_7,
        UC_MIPS_REG_8,
        UC_MIPS_REG_9,
        UC_MIPS_REG_10,
        UC_MIPS_REG_11,
        UC_MIPS_REG_12,
        UC_MIPS_REG_13,
        UC_MIPS_REG_14,
        UC_MIPS_REG_15,
        UC_MIPS_REG_16,
        UC_MIPS_REG_17,
        UC_MIPS_REG_18,
        UC_MIPS_REG_19,
        UC_MIPS_REG_20,
        UC_MIPS_REG_21,
        UC_MIPS_REG_22,
        UC_MIPS_REG_23,
        UC_MIPS_REG_24,
        UC_MIPS_REG_25,
        UC_MIPS_REG_26,
        UC_MIPS_REG_27,
        UC_MIPS_REG_28,
        UC_MIPS_REG_29,
        UC_MIPS_REG_30,
        UC_MIPS_REG_31,

        //> DSP registers
        UC_MIPS_REG_DSPCCOND,
        UC_MIPS_REG_DSPCARRY,
        UC_MIPS_REG_DSPEFI,
        UC_MIPS_REG_DSPOUTFLAG,
        UC_MIPS_REG_DSPOUTFLAG16_19,
        UC_MIPS_REG_DSPOUTFLAG20,
        UC_MIPS_REG_DSPOUTFLAG21,
        UC_MIPS_REG_DSPOUTFLAG22,
        UC_MIPS_REG_DSPOUTFLAG23,
        UC_MIPS_REG_DSPPOS,
        UC_MIPS_REG_DSPSCOUNT,

        //> ACC registers
        UC_MIPS_REG_AC0,
        UC_MIPS_REG_AC1,
        UC_MIPS_REG_AC2,
        UC_MIPS_REG_AC3,

        //> COP registers
        UC_MIPS_REG_CC0,
        UC_MIPS_REG_CC1,
        UC_MIPS_REG_CC2,
        UC_MIPS_REG_CC3,
        UC_MIPS_REG_CC4,
        UC_MIPS_REG_CC5,
        UC_MIPS_REG_CC6,
        UC_MIPS_REG_CC7,

        //> FPU registers
        UC_MIPS_REG_F0,
        UC_MIPS_REG_F1,
        UC_MIPS_REG_F2,
        UC_MIPS_REG_F3,
        UC_MIPS_REG_F4,
        UC_MIPS_REG_F5,
        UC_MIPS_REG_F6,
        UC_MIPS_REG_F7,
        UC_MIPS_REG_F8,
        UC_MIPS_REG_F9,
        UC_MIPS_REG_F10,
        UC_MIPS_REG_F11,
        UC_MIPS_REG_F12,
        UC_MIPS_REG_F13,
        UC_MIPS_REG_F14,
        UC_MIPS_REG_F15,
        UC_MIPS_REG_F16,
        UC_MIPS_REG_F17,
        UC_MIPS_REG_F18,
        UC_MIPS_REG_F19,
        UC_MIPS_REG_F20,
        UC_MIPS_REG_F21,
        UC_MIPS_REG_F22,
        UC_MIPS_REG_F23,
        UC_MIPS_REG_F24,
        UC_MIPS_REG_F25,
        UC_MIPS_REG_F26,
        UC_MIPS_REG_F27,
        UC_MIPS_REG_F28,
        UC_MIPS_REG_F29,
        UC_MIPS_REG_F30,
        UC_MIPS_REG_F31,

        UC_MIPS_REG_FCC0,
        UC_MIPS_REG_FCC1,
        UC_MIPS_REG_FCC2,
        UC_MIPS_REG_FCC3,
        UC_MIPS_REG_FCC4,
        UC_MIPS_REG_FCC5,
        UC_MIPS_REG_FCC6,
        UC_MIPS_REG_FCC7,

        //> AFPR128
        UC_MIPS_REG_W0,
        UC_MIPS_REG_W1,
        UC_MIPS_REG_W2,
        UC_MIPS_REG_W3,
        UC_MIPS_REG_W4,
        UC_MIPS_REG_W5,
        UC_MIPS_REG_W6,
        UC_MIPS_REG_W7,
        UC_MIPS_REG_W8,
        UC_MIPS_REG_W9,
        UC_MIPS_REG_W10,
        UC_MIPS_REG_W11,
        UC_MIPS_REG_W12,
        UC_MIPS_REG_W13,
        UC_MIPS_REG_W14,
        UC_MIPS_REG_W15,
        UC_MIPS_REG_W16,
        UC_MIPS_REG_W17,
        UC_MIPS_REG_W18,
        UC_MIPS_REG_W19,
        UC_MIPS_REG_W20,
        UC_MIPS_REG_W21,
        UC_MIPS_REG_W22,
        UC_MIPS_REG_W23,
        UC_MIPS_REG_W24,
        UC_MIPS_REG_W25,
        UC_MIPS_REG_W26,
        UC_MIPS_REG_W27,
        UC_MIPS_REG_W28,
        UC_MIPS_REG_W29,
        UC_MIPS_REG_W30,
        UC_MIPS_REG_W31,

        UC_MIPS_REG_HI,
        UC_MIPS_REG_LO,

        UC_MIPS_REG_P0,
        UC_MIPS_REG_P1,
        UC_MIPS_REG_P2,

        UC_MIPS_REG_MPL0,
        UC_MIPS_REG_MPL1,
        UC_MIPS_REG_MPL2,

        UC_MIPS_REG_ENDING, // <-- mark the end of the list or registers

        // alias registers
        UC_MIPS_REG_ZERO = UC_MIPS_REG_0,
        UC_MIPS_REG_AT = UC_MIPS_REG_1,
        UC_MIPS_REG_V0 = UC_MIPS_REG_2,
        UC_MIPS_REG_V1 = UC_MIPS_REG_3,
        UC_MIPS_REG_A0 = UC_MIPS_REG_4,
        UC_MIPS_REG_A1 = UC_MIPS_REG_5,
        UC_MIPS_REG_A2 = UC_MIPS_REG_6,
        UC_MIPS_REG_A3 = UC_MIPS_REG_7,
        UC_MIPS_REG_T0 = UC_MIPS_REG_8,
        UC_MIPS_REG_T1 = UC_MIPS_REG_9,
        UC_MIPS_REG_T2 = UC_MIPS_REG_10,
        UC_MIPS_REG_T3 = UC_MIPS_REG_11,
        UC_MIPS_REG_T4 = UC_MIPS_REG_12,
        UC_MIPS_REG_T5 = UC_MIPS_REG_13,
        UC_MIPS_REG_T6 = UC_MIPS_REG_14,
        UC_MIPS_REG_T7 = UC_MIPS_REG_15,
        UC_MIPS_REG_S0 = UC_MIPS_REG_16,
        UC_MIPS_REG_S1 = UC_MIPS_REG_17,
        UC_MIPS_REG_S2 = UC_MIPS_REG_18,
        UC_MIPS_REG_S3 = UC_MIPS_REG_19,
        UC_MIPS_REG_S4 = UC_MIPS_REG_20,
        UC_MIPS_REG_S5 = UC_MIPS_REG_21,
        UC_MIPS_REG_S6 = UC_MIPS_REG_22,
        UC_MIPS_REG_S7 = UC_MIPS_REG_23,
        UC_MIPS_REG_T8 = UC_MIPS_REG_24,
        UC_MIPS_REG_T9 = UC_MIPS_REG_25,
        UC_MIPS_REG_K0 = UC_MIPS_REG_26,
        UC_MIPS_REG_K1 = UC_MIPS_REG_27,
        UC_MIPS_REG_GP = UC_MIPS_REG_28,
        UC_MIPS_REG_SP = UC_MIPS_REG_29,
        UC_MIPS_REG_FP = UC_MIPS_REG_30,
        UC_MIPS_REG_S8 = UC_MIPS_REG_30,
        UC_MIPS_REG_RA = UC_MIPS_REG_31,

        UC_MIPS_REG_HI0 = UC_MIPS_REG_AC0,
        UC_MIPS_REG_HI1 = UC_MIPS_REG_AC1,
        UC_MIPS_REG_HI2 = UC_MIPS_REG_AC2,
        UC_MIPS_REG_HI3 = UC_MIPS_REG_AC3,

        UC_MIPS_REG_LO0 = UC_MIPS_REG_HI0,
        UC_MIPS_REG_LO1 = UC_MIPS_REG_HI1,
        UC_MIPS_REG_LO2 = UC_MIPS_REG_HI2,
        UC_MIPS_REG_LO3 = UC_MIPS_REG_HI3,
    }

    public static uc_engine engine;

        public static uc_err uc_emu_start(uc_engine engine, long startAddr, long stopAddr, int v, int v2)
        {
            Console.WriteLine("start:" + startAddr + "  stop:" + stopAddr);
            engine.emulator.Start((ulong)startAddr, (ulong)stopAddr);
            return engine.err;
        }

        public delegate void hook_codes(uc_engine uc, ulong address, uint size, object user_data);
        public delegate bool hook_mem_invalids(uc_engine uc, uc_mem_type type, ulong address, int size, long value, object user_data);
        static MemoryHookCallback hook_codesCallback;
        static MemoryHookCallback hook_mem_invalidsCallback;

        public static void codes(Emulator emulator, MemoryType type, ulong address, int size, ulong value, object userToken)
        {
            HC(Uc.engine, address, (uint)size, value);
        }

        public static void mem_invalids(Emulator emulator, MemoryType type, ulong address, int size, ulong value, object userToken)
        {
            HM(Uc.engine, (uc_mem_type)type, address, size, (long)value, userToken);
        }

        public static hook_codes HC;
        public static hook_mem_invalids HM;

        public static void HookBlock(Emulator emu, ulong address, int size, object userToken)
        {
            Console.WriteLine($">>> Tracing basic block at 0x{address.ToString("x2")}, block size = 0x{size.ToString("x2")}");
        }

        public static void HookCode(Emulator emu, ulong address, int size, object userToken)
        {

            Console.WriteLine($">>> Tracing instruction at 0x{address.ToString("x2")}, instruction size = 0x{size.ToString("x2")}");

            HC(Uc.engine, address, (uint)size, userToken);
        }

        private static void HookInterrupt(Emulator emulator, int into, object userData)
        {
            HM(Uc.engine, uc_mem_type.UC_MEM_READ, (ulong)into, 0, 0, userData);
        }

        public static void uc_hook_add(uc_engine engine, uc_hook trace, int mode, hook_mem_invalids hook_Mem, object obj, int v, int v2, int v3)
        {
            HM += hook_Mem;
            hook_mem_invalidsCallback += mem_invalids;
            engine.emulator.Hooks.Memory.Add((MemoryHookType)mode, hook_mem_invalidsCallback, obj);
            //engine.emulator.Hooks..Interrupt.Add(HookInterrupt, obj);
        }

        public static void uc_hook_add(uc_engine engine, uc_hook trace, int mode, hook_codes hook_Mem, object obj, int v, int v2)
        {
            HC += hook_Mem;
            hook_codesCallback += codes;
            //engine.emulator.Hooks.Code.Add((MemoryHookType)mode, hook_codesCallback, obj);
            engine.emulator.Hooks.Code.Add(HookCode, obj);
        }

        public static void uc_close(uc_engine engine)
        {

        }

        public static uc_err uc_mem_map_ptr(uc_engine engine, long v, int v2, uc_prot prot, byte* mem)
        {
            engine.emulator.Memory.Map((ulong)v, v2, MemoryPermissions.All);

            return engine.err;
        }

        public static string uc_strerror(uc_err err)
        {
            return "uc_strerror";
        }

        public static uc_err uc_open(int i, int m, out uc_engine Engine)
        {
            Engine = new uc_engine();
            Uc.engine = Engine;
            return Uc.engine.err;
        }

        public static void uc_reg_read(uc_engine engine, int id, out int v)
        {
            uint a;
            v = (int)uc_reg_read(engine, id, out a);
        }

        public static uint uc_reg_read(uc_engine engine, int id, out uint v)
        {
            v = (uint)engine.emulator.Registers.Read((int)id);
            return v;
        }

        public static uc_err uc_reg_write(uc_engine engine, int id, long value)
        {
            engine.emulator.Registers.Write((int)id, value);
        return uc_err.UC_ERR_OK;
    }

        public static void uc_mem_read(uc_engine engine, uint addr, out uint v, int size)
        {
            byte[] date = new byte[size];
            engine.emulator.Memory.Read((uint)addr, date, size);
            v = *(uint*)(getBytePtr(date));
        }

        public static void uc_mem_read(uc_engine engine, uint addr, sbyte* v, int size)
        {
            byte[] date = new byte[size];
            engine.emulator.Memory.Read((uint)addr, date, size);
            v = (sbyte*)(getBytePtr(date));
        }

        public static uc_err uc_mem_write(uc_engine engine, long addr, byte[] bytes, int size)
        {
            engine.emulator.Memory.Write((ulong)addr, bytes, (int)size);
            return engine.err;
        }

        public static uc_err uc_mem_write(uc_engine engine, long addr, void* bytes, uint size)
        {
            engine.emulator.Memory.Write((ulong)addr, Getbyte((byte*)bytes, size), (int)size);
            return engine.err;
        }

        public static byte[] Getbyte(byte* bp, uint len)
        {
            if (len == 4)
            {
                uint num = *(uint*)bp;
                byte[] bytes = BitConverter.GetBytes(num);
                //return bytes;
            }
            byte[] barr = new byte[len];
            Marshal.Copy((IntPtr)bp, barr, 0, (int)len);
            for (int i = 0; i < 4; i++)
            {
                byte b = barr[i];
            }

            //memcpy(bp,0, barr, 0,len);
            return barr;
        }



        public static int pthread_mutex_init(Mutex mutex, object o)
        {
            if (mutex.InitializeLifetimeService() == o)
                return -1;
            else
                return 0;
        }

        public static int pthread_mutex_lock(Mutex mutex)
        {
            if (mutex.WaitOne())
                return -1;
            else
                return 0;
        }

        public static int pthread_mutex_unlock(Mutex mutex)
        {
            mutex.ReleaseMutex();
            return 0;
        }

        public static void perror(string info)
        {
            Console.Write(info);
        }
    }

    public enum uc_arm_reg
    {
        UC_ARM_REG_INVALID = 0,
        UC_ARM_REG_APSR,
        UC_ARM_REG_APSR_NZCV,
        UC_ARM_REG_CPSR,
        UC_ARM_REG_FPEXC,
        UC_ARM_REG_FPINST,
        UC_ARM_REG_FPSCR,
        UC_ARM_REG_FPSCR_NZCV,
        UC_ARM_REG_FPSID,
        UC_ARM_REG_ITSTATE,
        UC_ARM_REG_LR,
        UC_ARM_REG_PC,
        UC_ARM_REG_SP,
        UC_ARM_REG_SPSR,
        UC_ARM_REG_D0,
        UC_ARM_REG_D1,
        UC_ARM_REG_D2,
        UC_ARM_REG_D3,
        UC_ARM_REG_D4,
        UC_ARM_REG_D5,
        UC_ARM_REG_D6,
        UC_ARM_REG_D7,
        UC_ARM_REG_D8,
        UC_ARM_REG_D9,
        UC_ARM_REG_D10,
        UC_ARM_REG_D11,
        UC_ARM_REG_D12,
        UC_ARM_REG_D13,
        UC_ARM_REG_D14,
        UC_ARM_REG_D15,
        UC_ARM_REG_D16,
        UC_ARM_REG_D17,
        UC_ARM_REG_D18,
        UC_ARM_REG_D19,
        UC_ARM_REG_D20,
        UC_ARM_REG_D21,
        UC_ARM_REG_D22,
        UC_ARM_REG_D23,
        UC_ARM_REG_D24,
        UC_ARM_REG_D25,
        UC_ARM_REG_D26,
        UC_ARM_REG_D27,
        UC_ARM_REG_D28,
        UC_ARM_REG_D29,
        UC_ARM_REG_D30,
        UC_ARM_REG_D31,
        UC_ARM_REG_FPINST2,
        UC_ARM_REG_MVFR0,
        UC_ARM_REG_MVFR1,
        UC_ARM_REG_MVFR2,
        UC_ARM_REG_Q0,
        UC_ARM_REG_Q1,
        UC_ARM_REG_Q2,
        UC_ARM_REG_Q3,
        UC_ARM_REG_Q4,
        UC_ARM_REG_Q5,
        UC_ARM_REG_Q6,
        UC_ARM_REG_Q7,
        UC_ARM_REG_Q8,
        UC_ARM_REG_Q9,
        UC_ARM_REG_Q10,
        UC_ARM_REG_Q11,
        UC_ARM_REG_Q12,
        UC_ARM_REG_Q13,
        UC_ARM_REG_Q14,
        UC_ARM_REG_Q15,
        UC_ARM_REG_R0,
        UC_ARM_REG_R1,
        UC_ARM_REG_R2,
        UC_ARM_REG_R3,
        UC_ARM_REG_R4,
        UC_ARM_REG_R5,
        UC_ARM_REG_R6,
        UC_ARM_REG_R7,
        UC_ARM_REG_R8,
        UC_ARM_REG_R9,
        UC_ARM_REG_R10,
        UC_ARM_REG_R11,
        UC_ARM_REG_R12,
        UC_ARM_REG_S0,
        UC_ARM_REG_S1,
        UC_ARM_REG_S2,
        UC_ARM_REG_S3,
        UC_ARM_REG_S4,
        UC_ARM_REG_S5,
        UC_ARM_REG_S6,
        UC_ARM_REG_S7,
        UC_ARM_REG_S8,
        UC_ARM_REG_S9,
        UC_ARM_REG_S10,
        UC_ARM_REG_S11,
        UC_ARM_REG_S12,
        UC_ARM_REG_S13,
        UC_ARM_REG_S14,
        UC_ARM_REG_S15,
        UC_ARM_REG_S16,
        UC_ARM_REG_S17,
        UC_ARM_REG_S18,
        UC_ARM_REG_S19,
        UC_ARM_REG_S20,
        UC_ARM_REG_S21,
        UC_ARM_REG_S22,
        UC_ARM_REG_S23,
        UC_ARM_REG_S24,
        UC_ARM_REG_S25,
        UC_ARM_REG_S26,
        UC_ARM_REG_S27,
        UC_ARM_REG_S28,
        UC_ARM_REG_S29,
        UC_ARM_REG_S30,
        UC_ARM_REG_S31,

        UC_ARM_REG_C1_C0_2,
        UC_ARM_REG_C13_C0_2,
        UC_ARM_REG_C13_C0_3,

        UC_ARM_REG_IPSR,
        UC_ARM_REG_MSP,
        UC_ARM_REG_PSP,
        UC_ARM_REG_CONTROL,
        UC_ARM_REG_ENDING,      // <-- mark the end of the list or registers

        //> alias registers
        UC_ARM_REG_R13 = UC_ARM_REG_SP,
        UC_ARM_REG_R14 = UC_ARM_REG_LR,
        UC_ARM_REG_R15 = UC_ARM_REG_PC,

        UC_ARM_REG_SB = UC_ARM_REG_R9,
        UC_ARM_REG_SL = UC_ARM_REG_R10,
        UC_ARM_REG_FP = UC_ARM_REG_R11,
        UC_ARM_REG_IP = UC_ARM_REG_R12,
    }

    public enum uc_err_type
    {
        UC_ERR_OK = 0,   // No error: everything was fine
        UC_ERR_NOMEM,      // Out-Of-Memory error: uc_open(), uc_emulate()
        UC_ERR_ARCH,     // Unsupported architecture: uc_open()
        UC_ERR_HANDLE,   // Invalid handle
        UC_ERR_MODE,     // Invalid/unsupported mode: uc_open()
        UC_ERR_VERSION,  // Unsupported version (bindings)
        UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start()
        UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start()
        UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start()
        UC_ERR_HOOK,    // Invalid hook type: uc_hook_add()
        UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
        UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
        UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start()
        UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start()
        UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start()
        UC_ERR_ARG,     // Inavalid argument provided to uc_xxx function (See specific function API)
        UC_ERR_READ_UNALIGNED,  // Unaligned read
        UC_ERR_WRITE_UNALIGNED,  // Unaligned write
        UC_ERR_FETCH_UNALIGNED,  // Unaligned fetch
        UC_ERR_HOOK_EXIST,  // hook for this event already existed
        UC_ERR_RESOURCE,    // Insufficient resource: uc_emu_start()
        UC_ERR_EXCEPTION, // Unhandled CPU exception
    }

    //[StructLayout(LayoutKind.Sequential)]
    public class uc_engine
    {
        public uc_err err;
        public uc_hook hook;
        public Emulator emulator;
        public uc_engine()
        {
            emulator = new ArmEmulator(ArmMode.Arm);
        }
    }

    //public class uc_err
    //{
    //    public uc_err_type type;
    //    public static implicit operator uc_err(uc_err_type type)
    //    {
    //        return new uc_err() { type = type  };
    //    }
    //}

    public class uc_hook
    {

    }

    //public enum uc_mem_type
    //{
    //    UC_MEM_READ = 16,

    //    UC_MEM_WRITE,

    //    UC_MEM_FETCH,

    //    UC_MEM_READ_UNMAPPED,

    //    UC_MEM_WRITE_UNMAPPED,

    //    UC_MEM_FETCH_UNMAPPED,

    //    UC_MEM_WRITE_PROT,

    //    UC_MEM_READ_PROT,

    //    UC_MEM_FETCH_PROT,

    //    UC_MEM_READ_AFTER,
    //}

    public enum uc_prot
    {
        UC_PROT_ALL = 7
    }
